#include "kernel.h"
#include "common.h"

/*
* | 虚拟地址 | VPN[1] (10 位) | VPN[0] (10 位) | 偏移 (12 位) |
*/
void map_page(uint32_t* table1, vaddr_t vaddr, paddr_t paddr, uint32_t flags) {
    if (!is_aligned(vaddr, PAGE_SIZE) || !is_aligned(paddr, PAGE_SIZE)) {
        PANIC("map_page: vaddr(%x) or paddr(%x) is not aligned", vaddr, paddr);
    }

    uint32_t vpn1 = (vaddr >> 22) & 0x3ff;
    if (!(table1[vpn1] & PAGE_V)) {
        //创建二级页表
        paddr_t paddr1 = alloc_pages(1);
        table1[vpn1] = ((paddr1 / PAGE_SIZE) << 10) | PAGE_V;
    }
    //设置二级页表项映射物理页面
    uint32_t vpn2 = (vaddr >> 12) & 0x3ff;
    uint32_t* table2 = (uint32_t*)((table1[vpn1] >> 10) * PAGE_SIZE);
    table2[vpn2] = ((paddr / PAGE_SIZE) << 10) | flags | PAGE_V;
}

extern char __bss[], __bss_end[], __stack_top[];

/**
 * @brief 执行SBI（Supervisor Binary Interface）调用
 *
 * 该函数用于执行SBI调用，通过设置特定的寄存器并触发`ecall`指令来与底层固件或操作系统进行交互。
 *
 * @param arg0 传递给SBI调用的第一个参数
 * @param arg1 传递给SBI调用的第二个参数
 * @param arg2 传递给SBI调用的第三个参数
 * @param arg3 传递给SBI调用的第四个参数
 * @param arg4 传递给SBI调用的第五个参数
 * @param arg5 传递给SBI调用的第六个参数
 * @param fid 功能ID，指定要执行的具体SBI功能
 * @param eid 扩展ID，指定要使用的SBI扩展
 * @return struct sbiret 返回一个包含SBI调用结果的结构体
 */
struct sbiret sbi_call(long arg0, long arg1, long arg2, long arg3, long arg4,
    long arg5, long fid, long eid) {
    // register 将参数赋值给对应的寄存器，用于SBI调用
    register long a0 __asm__("a0") = arg0;
    register long a1 __asm__("a1") = arg1;
    register long a2 __asm__("a2") = arg2;
    register long a3 __asm__("a3") = arg3;
    register long a4 __asm__("a4") = arg4;
    register long a5 __asm__("a5") = arg5;
    register long a6 __asm__("a6") = fid;
    register long a7 __asm__("a7") = eid;

    // 执行SBI调用，触发ecall指令
    __asm__ volatile(
        "ecall\n"  // 触发SBI调用
        : "=r"(a0), "=r"(a1) // 输出操作数，将a0和a1寄存器的值保存到变量中
        // 输入操作数，将所有参数传递给ecall指令
        : "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7)
        : "memory");// 内存破坏者，告诉编译器ecall可能会修改内存

    // 返回包含SBI调用结果的结构体
    return (struct sbiret) { a0, a1 };
}

void putchar(char c) {
    sbi_call(c, 0, 0, 0, 0, 0, 0, 1);
}

extern char __free_ram[], __free_ram_end[];
paddr_t alloc_pages(uint32_t n) {
    static paddr_t current = (paddr_t)__free_ram;
    paddr_t ret = current;
    current += n * PAGE_SIZE;
    if (current > (paddr_t)__free_ram_end) {
        PANIC("alloc_pages: out of memory");
    }
    memset((void*)ret, 0, n * PAGE_SIZE);
    return ret;
}

__attribute__((naked)) void switch_context(uint32_t* prev_sp, uint32_t* next_sp) {
    __asm__ __volatile__(
        "addi sp , sp, -13*4\n" //为13个4字节寄存器分配栈空间
        "sw ra, 0*4(sp)\n" //仅保存被调用者保存的寄存器

        //在 RISC-V 中，s0到s11是被调用者保存的寄存器。
        //其他像a0这样的寄存器是调用者保存的寄存器，已经由调用者保存在栈上
        "sw s0, 1*4(sp)\n"
        "sw s1, 2*4(sp)\n"
        "sw s2, 3*4(sp)\n"
        "sw s3, 4*4(sp)\n"
        "sw s4, 5*4(sp)\n"
        "sw s5, 6*4(sp)\n"
        "sw s6, 7*4(sp)\n"
        "sw s7, 8*4(sp)\n"
        "sw s8, 9*4(sp)\n"
        "sw s9, 10*4(sp)\n"
        "sw s10, 11*4(sp)\n"
        "sw s11, 12*4(sp)\n"

        //切换栈指针
        "sw sp, (a0)\n" // *prev_sp = sp;
        "lw sp, (a1)\n" // 切换栈指针(sp)

        // 从下一个进程的栈中恢复寄存器
        "lw ra, 0*4(sp)\n"//仅恢复被调用者保存的寄存器
        "lw s0, 1*4(sp)\n"
        "lw s1, 2*4(sp)\n"
        "lw s2, 3*4(sp)\n"
        "lw s3, 4*4(sp)\n"
        "lw s4, 5*4(sp)\n"
        "lw s5, 6*4(sp)\n"
        "lw s6, 7*4(sp)\n"
        "lw s7, 8*4(sp)\n"
        "lw s8, 9*4(sp)\n"
        "lw s9, 10*4(sp)\n"
        "lw s10, 11*4(sp)\n"
        "lw s11, 12*4(sp)\n"
        "addi sp, sp, 13*4\n" // 释放栈空间
        "ret\n" // 返回被调用者
    );
}

process process_table[PROCESS_MAX_NUM];
extern char __kernel_base[];

process* create_process(uint32_t pc) {
    for (int i = 0; i < PROCESS_MAX_NUM; i++) {
        if (process_table[i].state == PROC_UNUSED) {
            process* proc = &process_table[i];
            // 初始化栈，保存CPU寄存器、返回地址和局部变量
            // 将进程栈顶部向下 48 字节（4 * 12）的内存区域清零，这部分空间通常用于保存被调用者保存的寄存器。
            memset(&proc->stack[sizeof(proc->stack) - 4 * 12], 0, 4 * 12);
            // 把新进程的入口点 pc 的值复制到栈顶向下 52 字节（4 * 13）的位置，该位置将作为进程切换时的返回地址。
            memcpy(&proc->stack[sizeof(proc->stack) - 4 * 13], &pc, sizeof(uint32_t));
            // *(uint32_t*)(&proc->stack[sizeof(proc->stack) - 4 * 13]) = pc;

            // 初始化页表，将进程的虚拟地址映射到物理地址
            // 为进程分配一个页表，用于将虚拟地址映射到物理地址
            // 遍历从内核基地址到物理内存结束地址的所有物理页面，为每个页面分配一个页表项
            // 将每个物理页面的地址映射到虚拟地址空间中的相同位置，同时设置页表项的访问权限为可读、可写和可执行
            proc->page_table = (uint32_t*)alloc_pages(1);
            for (paddr_t paddr = (paddr_t)__kernel_base; paddr < (paddr_t)__free_ram_end; paddr += PAGE_SIZE) {
                map_page(proc->page_table, paddr, paddr, PAGE_X | PAGE_R | PAGE_W);
            }

            // 将进程的栈指针 sp 设置为栈顶向下 52 字节的位置，这样在进程切换时，会从该位置开始恢复寄存器和返回地址
            proc->sp = (vaddr_t)&proc->stack[sizeof(proc->stack) - 4 * 13];
            proc->pid = i + 1;
            proc->state = PROC_RUNNABLE;
            return proc;
        }
    }
    printf("create_process: no more process\n");
    return NULL;
}

void delay(uint32_t n) {
    for (uint32_t i = 0; i < n; i++) {
        for (uint32_t j = 0; j < 100000000; j++) {
            __asm__ volatile("nop");
        }
    }
}

process* current_process;
process* idle_process;
void yield() {
    for (int i = 0;i < PROCESS_MAX_NUM;i++) {
        // 从当前进程开始环式寻找下一个可运行的进程
        process* proc_next = &process_table[(current_process->pid + i) % PROCESS_MAX_NUM];
        if (proc_next->state == PROC_RUNNABLE && proc_next->pid > 0 && proc_next != current_process) {
            __asm__ __volatile__(
                "sfence.vma\n" // 内存屏障，确保内存操作的顺序性
                "csrw satp, %[satp]\n" // 读取并写入satp寄存器，设置页表基址寄存器satp
                "sfence.vma\n"
                "csrw sscratch, %[sscratch]\n" // 读取并写入sscratch寄存器，设置页表基址寄存器sscratch
                :
            : [satp] "r" (SATP_SV32 | ((paddr_t)proc_next->page_table / PAGE_SIZE)),
                [sscratch] "r" ((paddr_t)&proc_next->stack[sizeof(proc_next->stack)])
                );

            process* proc_prev = current_process;
            current_process = proc_next;
            switch_context(&proc_prev->sp, &proc_next->sp);
            break;
        }
    }
}

process* processA;
process* processB;

void processA_entry() {
    printf("processA\n");
    while (1) {
        printf("A");
        delay(1);
        yield();
    }
}
void processB_entry() {
    printf("processB\n");
    while (1) {
        printf("B");
        delay(1);
        yield();
    }
}

void idle() {
    printf("system idle\n");
}

void kernel_main() {
    memset(__bss, 0, __bss_end - __bss);

    idle_process = create_process((uint32_t)idle);
    idle_process->pid = 0;
    current_process = idle_process;

    processA = create_process((uint32_t)processA_entry);
    processB = create_process((uint32_t)processB_entry);
    yield();

    char* str = "\nHello, world!\n";
    for (int i = 0; str[i] != '\0'; i++)
    {
        putchar(str[i]);
    }

    paddr_t addr1 = alloc_pages(2);
    paddr_t addr2 = alloc_pages(1);
    printf("alloc_pages: addr1 = %x, addr2 = %x\n", addr1, addr2);

    PANIC("panic test");

    printf("Hello, %s, count %d to %d, then %c to %c \n%",
        "qmue", -10, 10, 'a', 'z');
    printf("\n");
    for (;;) {
        __asm__ volatile("wfi\n"); // Wait for interrupt，是 RISC-V 架构的指令，
    }
}

__attribute__((section(".text.boot")))
__attribute__((naked)) void boot() {
    __asm__ volatile(
        "mv sp, %[stack_top]\n" // Set the stack pointer
        "j kernel_main\n"       // Jump to the kernel main function
        :
    : [stack_top] "r" (__stack_top) // Pass the stack top address as %[stack_top]
        );
}